#!/usr/bin/env bash

egrep_q() {
  egrep "$@" >/dev/null 2>/dev/null
}

die() {
  echo 'pre-commit hook failure' 1>&2
  echo '-----------------------' 1>&2
  echo '' 1>&2
  echo "$@" 1>&2
  exit 1
}

ExternalData_stage_linked_content() {
  # Identify the hash algorithm used.
  case "$file" in
    *.md5) algo=MD5 ; base="${file/.md5}" ; validate="^[0-9a-fA-F]{32}$" ;;
    *.sha512) algo=SHA512 ; base="${file/.sha512}" ; validate="^[0-9a-fA-F]{128}$" ;;
    *) die "$file: invalid content link (unrecognized extension)" ;;
  esac

  # Load and validate the hash stored in the staged blob.
  hash=$(git cat-file blob $dst_obj) || hash=""
  echo "$hash" | egrep_q "$validate" ||
  die "$file: invalid content link (does not match '$validate')"

  # Reject simultaneous raw file and content link.
  files=$(git ls-files -- "$base")
  if test -n "$files"; then
    die "$file: content link may not coexist with $files"
  fi

  # Find the content referenced by the link.
  staged="$(dirname "$file")/.ExternalData_${algo}_${hash}"
  stored="${ExternalData_STORE}/$algo/$hash"
  ref="refs/data/$algo/$hash"
  obj=$(git rev-parse --verify -q "$ref") || obj=""
  if test -z "$obj" -a -f "$staged"; then
    # Content is staged by the ExternalData module.  Store it in Git.
    obj=$(git hash-object --no-filters -w -- "$staged") ||
    die "$file: git hash-object failed to load $staged"
    git update-ref "$ref" "$obj" "" ||
    die "$file: git update-ref failed to create $ref = $obj"
    echo "$file: Added content to Git at $ref"
  fi

  # Move staged object to local store if it is in Git.
  if test -f "$staged" && test -n "$obj"; then
    mkdir -p "${stored%/*}" &&
    mv "$staged" "$stored" &&
    rm -f "$staged" &&
    echo "$file: Added content to local store at $stored"
  fi

  # Report destination of content link.
  if test -f "$stored"; then
    echo "Content link $file -> $stored"
  else
    echo "Content link $file -> (object not in local store)"
  fi
}

ExternalData_non_content_link() {
  # Reject simultaneous raw file and content link.
  files=$(git ls-files -- "$file.md5" "$file.sha512")
  if test -n "$files"; then
    die "$file: file may not coexist with $files"
  fi
}

#-----------------------------------------------------------------------------

# Check that developmer setup is up-to-date.
lastSetupForDevelopment=$(git config --get hooks.SetupForDevelopment || echo 0)
eval $(grep '^SetupForDevelopment_VERSION=' "${BASH_SOURCE%/*}/../SetupForDevelopment.sh")
test -n "$SetupForDevelopment_VERSION" || SetupForDevelopment_VERSION=0
if test $lastSetupForDevelopment -lt $SetupForDevelopment_VERSION; then
  die 'Developer setup in this work tree is out of date.  Please re-run

  Utilities/SetupForDevelopment.sh
'
fi

#-----------------------------------------------------------------------------

# Local ExternalData object repository.
ExternalData_STORE=".ExternalData"

# Process content links created by/for the CMake ExternalData module.
git diff-index --cached HEAD --diff-filter=AM |
while read src_mode dst_mode src_obj dst_obj status file; do
  if echo "$dst_mode $file" | egrep_q '^100644 .*\.(md5|sha512)$'; then
    ExternalData_stage_linked_content
  else
    ExternalData_non_content_link
  fi
done || exit 1
